/**
 * 获取需要嵌入的新分段
 *
 * @param {Array} history - 聊天历史记录数组
 *
 * @returns {Array<{key: string, role: string, content: string, start: number, end: number}>} - 需要嵌入的新分段数组
 */
function vectorGetNewTextsToEmbed(history) {
    /** 用于存储需要嵌入的新分段 */
    const newTexts = [];
    // 遍历聊天历史记录
    for (const msg of history) {
        // 如果消息内容为空或仅包含空白字符，则跳过当前消息
        if (!msg.content || !msg.content.trim()) continue;
        /** 将消息内容分割为多个分段对象 */
        const segments = splitTextToObjects(msg.content);
        // 遍历每个分段
        for (const [index, segment] of segments.entries()) {
            /** 从分段对象中解构出文本内容、起始位置和结束位置 */
            const { text, start, end } = segment;
            /** 生成当前分段的唯一键 */
            const key = vectorMakeSegmentKey(msg.role, text, index, start, end);
            // 如果向量存储中不存在该键，则表示该分段是新的，需要嵌入
            if (!vectorStore.has(key)) {
                // 将新分段信息添加到结果数组中
                newTexts.push({ key, role: msg.role, content: text, start, end });
            }
        }
    }
    // 返回需要嵌入的新分段数组
    return newTexts;
}

/**
 * 同步数据库与当前历史（清理过期分段，但保留 .page 文件）
 *
 * @param {Array} history - 聊天历史记录数组
 */
function vectorSyncWithHistory(history) {
    /** 用于存储当前历史记录中所有分段的唯一键 */
    const currentKeys = new Set();
    // 遍历聊天历史记录
    for (const msg of history) {
        // 如果消息内容为空或仅包含空白字符，则跳过当前消息
        if (!msg.content || !msg.content.trim()) continue;
        /** 将消息内容分割为多个分段对象 */
        const segments = splitTextToObjects(msg.content);
        // 遍历每个分段
        for (const [index, segment] of segments.entries()) {
            /** 从分段对象中解构出文本内容、起始位置和结束位置 */
            const { text, start, end } = segment;
            /** 生成当前分段的唯一键 */
            const key = vectorMakeSegmentKey(msg.role, text, index, start, end);
            // 将生成的唯一键添加到集合中
            currentKeys.add(key);
        }
    }
    // 遍历向量存储中的所有键
    for (const key of vectorStore.keys()) {
        // 如果键以 'knowledge:page:' 开头，则跳过，保留 .page 文件
        if (key.startsWith('knowledge:page:')) continue;
        // 如果当前历史记录中不包含该键，则表示该分段已过期，从向量存储中删除
        if (!currentKeys.has(key)) vectorStore.delete(key);
    }
}

/**
 * 添加新嵌入向量
 * @param {Array} history - 聊天历史记录数组
 */
async function vectorAddNewEmbeddings(history) {
    /** 获取需要嵌入的新分段 */
    const newSegments = vectorGetNewTextsToEmbed(history);
    // 如果没有新分段需要嵌入，直接返回
    if (newSegments.length === 0) return;
    /** 从新分段中提取需要生成嵌入向量的文本内容 */
    const texts = newSegments.map(s => s.content);
    /** 异步获取这些文本的嵌入向量 */
    const embeddings = await vectorGetEmbedding(texts);
    // 遍历每个嵌入向量，将其与对应的分段信息一起添加到向量存储中
    embeddings.forEach(
        (emb, i) => {
            /** 获取当前嵌入向量对应的分段信息 */
            const seg = newSegments[i];
            // 将分段的键、角色、内容、嵌入向量、起始位置和结束位置添加到向量存储中
            vectorAdd(seg.key, seg.role, seg.content, emb, seg.start, seg.end);
        }
    );
}